<template>
  <div>
    <div v-self-loading="loadingStatus" style="background:red;height:300px;" show-suc="true" @on-refresh="fresh">p标签</div>
  </div>
</template>

<script>
function handleLoading(el, {
  value,
  oldValue
}, vnode) {
  if (value !== oldValue) {
    // 只在指令绑定的值改变的情况下进行操作
    const POS = window.getComputedStyle(el, null).position;
    if (POS === "" || POS === "static") {
      el.style.position = "relative";
    }
    const showSuc = el.getAttribute("show-suc") || false,
      loadingText = el.getAttribute("loading-text") || "加载中...",
      failedText = el.getAttribute("failed-text") || "加载失败，请稍后重试",
      sucText = el.getAttribute("suc-text") || "导入成功";

    removeLoadingHTML(el);
    switch (value) {
      case "loading":
        el.append(createLoadingHTML(value, loadingText));
        break;
      case "suc":
        if (showSuc) {
          const maskElement = createLoadingHTML(value, sucText);
          maskElement.addEventListener("click", e => {
            removeLoadingHTML(el);
          });
          el.append(maskElement);
          setTimeout(() => {
            removeLoadingHTML(el);
          }, 3000);
        }
        break;
      case "failed":
        el.append(createLoadingHTML(value, failedText));
        // 如果定义了重新加载事件则增加按钮元素并绑定事件
        if (vnode.data.on && typeof vnode.data.on["on-refresh"] === "function") {
          // 事件在 vnode.data.on 对象中
          const btn = document.createElement("button");
          btn.className = "btn-white";
          btn.innerHTML = "重新加载";
          btn.style.marginTop = "20px";
          btn.style.backgroundColor = "transparent";
          btn.addEventListener("click", vnode.data.on["on-refresh"]);
          el.querySelector(".loading-spinner").append(btn);
        }
        break;
    }
  }
}
function removeLoadingHTML(el) {
  const CLASS_NAME = "loading-mask";
  // 遮罩层元素 loading-mask 通常会是指令元素内的最后一个子元素
  let node = el.lastChild;
  while (node) {
    if (node.classList && node.classList.contains(CLASS_NAME)) {
      node.parentNode.removeChild(node);
      break;
    } else {
      node = node.previousSibling;
    }
  }
}
function createLoadingHTML(status, text) {
  // 创建loading的父子节点，并添加相应的class
  const CLASS_NAME = `is-${status}`;
  const loadingMask = document.createElement("div");
  loadingMask.className = "loading-mask";
  const loadingSpinner = document.createElement("div");
  loadingSpinner.className = "loading-spinner";
  loadingMask.append(loadingSpinner);
  const loadingIcon = document.createElement("div");
  loadingIcon.className = `loading-icon ${CLASS_NAME}`;
  if (status === "failed") {
    loadingIcon.className = `loading-icon ${CLASS_NAME}`;
  } else if (status === "suc") {
    loadingIcon.className = `loading-icon ${CLASS_NAME}`;
  }
  const loadingText = document.createElement("div");
  loadingText.textContent = text;
  loadingText.className = `loading-text ${CLASS_NAME}`;
  loadingSpinner.append(loadingIcon);
  loadingSpinner.append(loadingText);
  return loadingMask;
}
export default {
  name: 'A',
  data() {
    return {
      name: '我是A组件',
      loadingStatus:'loading'
    }
  },
  directives: {
    "self-loading": {
      inserted: handleLoading,
      update: handleLoading,
      unbind: function (el, binding) {
        removeLoadingHTML(el);
      }
    }
  },
  mounted(){
    setTimeout(() => {
      this.loadingStatus = 'failed'
    }, 2000);
  },
  methods:{
    fresh(){
      this.loadingStatus = 'loading'
      setTimeout(() => {
      this.loadingStatus = 'suc'
    }, 2000);
    }
  }
}
</script>

<style lang="scss">
$green: #1cc336;
$red: #f55959; // 错误配色
.loading-mask {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  z-index: 5;
  background-color: #fff;

  .loading-spinner {
    top: 50%;
    transform: translateY(-50%);
    width: 100%;
    text-align: center;
    position: absolute;

    .loading-icon {
      width: 75px;
      height: 75px;
      display: inline-block;
      $ball-radius: 11px;

      @keyframes animate {
        17% {
          border-bottom-right-radius: 3px;
        }

        25% {
          transform: translateY(9px) rotate(22.5deg);
        }

        50% {
          transform: translateY(17px) scale(1, .9) rotate(45deg);
          border-bottom-right-radius: 36px;
        }

        75% {
          transform: translateY(9px) rotate(67.5deg);
        }

        100% {
          transform: translateY(0) rotate(90deg);
        }
      }


      @keyframes shadow {

        0%,
        100% {
          transform: scale(1, 1);
        }

        50% {
          transform: scale(1.2, 1);
        }
      }

      &.is-loading {
        position: relative;
        width: 50px;
        height: 50px;

        &:before {
          content: '';
          width: 100%;
          height: 5px;
          background: #000;
          opacity: 0.1;
          position: absolute;
          top: 59px;
          left: 0;
          border-radius: 50%;
          animation: shadow .5s linear infinite;
        }

        &:after {
          content: '';
          display: block;
          width: 50px;
          height: 50px;
          background: linear-gradient(45deg, #3c84e1, #5cb1ff);
          animation: animate .5s linear infinite;
          border-radius: 3px;
        }
      }


      &.is-suc {
        color: $green;
        font-size: 55px;
        line-height: 75px;
        text-align: center;
      }

      &.is-failed {
        color: $red;
        text-align: center;
        line-height: 75px;
        font-size: 55px;
      }
    }

    // 列表空icon
    .icon-list-empty {
      width: 100%;
      line-height: 1;
      text-align: center;
      font-size: 80px;
      color: #d6dae7;
      opacity: 0.7;
    }

    .loading-text {
      margin-top: 25px;
      text-align: center;
      font-size: 14px;
      line-height: 1;
      letter-spacing: 1px;

      &.is-loading {
        color: #1d94f7;
      }

      &.is-suc {
        color: #22ac38;
      }

      &.is-failed {
        color: $red;
      }

      &.is-null {
        display: block;
        width: 100%;
        margin-top: 15px;
        font-size: 14px;
        line-height: 1;
        text-align: center;
        letter-spacing: 1px;
        color: #d6dae7;
        opacity: 0.7;
      }
    }
  }
}
</style>